/*
 * @(#)MultiEdge.java   09/09/16
 * 
 * Copyright (C) 2009 Ecole des Mines d'Ales
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: yulinz88@gmail.com
 *
 *
 *
 */



package fr.ema.arch3d.gea.ass;

import org.jgraph.graph.AbstractCellView;
import org.jgraph.graph.AttributeMap;
import org.jgraph.graph.CellView;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.Edge;
import org.jgraph.graph.EdgeView;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphLayoutCache;
import org.jgraph.graph.PortView;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import java.util.ArrayList;
import java.util.List;

//~--- classes ----------------------------------------------------------------

/**
 * A simple implementation for an edge.
 *
 * @version 1.0 1/1/02
 * @author Gaudenz Alder
 */
public class MultiEdge extends DefaultGraphCell implements Edge {

    /** Source and target of the edge. */
    protected Object source, target, source1, target1;

    /**
     * Constructs an empty edge.
     */
    public MultiEdge() {
        this(null);
    }

    /**
     * Constructs an edge that holds a reference to the specified user object.
     *
     * @param userObject
     *            reference to the user object
     */
    public MultiEdge(Object userObject) {
        this(userObject, null);
    }

    /**
     * Constructs an edge that holds a reference to the specified user object
     * and sets default values for points and the label position.
     *
     * @param userObject
     *            reference to the user object
     * @param storageMap
     */
    public MultiEdge(Object userObject, AttributeMap storageMap) {
        super(userObject, storageMap);
    }

    /**
     * Returns the source of the edge.
     */
    public Object getSource() {
        return source;
    }

    /**
     * Returns the target of the edge.
     */
    public Object getTarget() {
        return target;
    }

    /**
     * Sets the source of the edge.
     */
    public void setSource(Object port) {
        source = port;
    }

    /**
     * Returns the target of <code>edge</code>.
     */
    public void setTarget(Object port) {
        target = port;
    }

    /**
     * Create a clone of the cell. The cloning of the user object is deferred to
     * the cloneUserObject() method. The source and target references are set to
     * null.
     *
     * @return Object a clone of this object.
     */
    public Object clone() {
        MultiEdge edge = (MultiEdge) super.clone();

        edge.source = null;
        edge.target = null;

        return edge;
    }

    /**
     * Class description
     *
     *
     * @version        1.0, 09/09/13
     * @author         Huaxi (Yulin) ZHANG
     */
    public static class DefaultRouting extends LoopRouting {
        protected List routeEdge(GraphLayoutCache cache, EdgeView edge) {
            List    newPoints = new ArrayList();
            int     n         = edge.getPointCount();
            Point2D from      = edge.getPoint(0);

            newPoints.add(from);

            if (edge.getSource() instanceof PortView) {
                newPoints.set(0, edge.getSource());
                from = ((PortView) edge.getSource()).getLocation();
            } else if (edge.getSource() != null) {
                Rectangle2D b = edge.getSource().getBounds();

                from = edge.getAttributes().createPoint(b.getCenterX(), b.getCenterY());
            }

            Point2D  to     = edge.getPoint(n - 1);
            CellView target = edge.getTarget();

            if (target instanceof PortView) {
                to = ((PortView) target).getLocation();
            } else if (target != null) {
                Rectangle2D b = target.getBounds();

                to = edge.getAttributes().createPoint(b.getCenterX(), b.getCenterY());
            }

            if ((from != null) && (to != null)) {
                Point2D[] routed;
                double    dx = Math.abs(from.getX() - to.getX());
                double    dy = Math.abs(from.getY() - to.getY());
                double    x2 = from.getX() + ((to.getX() - from.getX()) / 2);
                double    y2 = from.getY() + ((to.getY() - from.getY()) / 2);

                routed = new Point2D[2];

                Rectangle2D targetBounds = null;
                Rectangle2D sourceBounds = null;

                if (((edge.getTarget() != null) && (edge.getTarget().getParentView() != null))
                        && ((edge.getSource() != null) && (edge.getSource().getParentView() != null))) {
                    targetBounds = edge.getTarget().getParentView().getBounds();
                    sourceBounds = edge.getSource().getParentView().getBounds();
                }

                if ((targetBounds != null) && (sourceBounds != null)) {
                    if (dx > dy) {
                        routed[0] = edge.getAttributes().createPoint(x2, from.getY());
                        routed[1] = edge.getAttributes().createPoint(x2, to.getY());

                        if (targetBounds.contains(routed[0]) || (sourceBounds.contains(routed[0]))
                                || targetBounds.contains(routed[1]) || (sourceBounds.contains(routed[1]))) {
                            routed[0] = edge.getAttributes().createPoint(from.getX(), y2);
                            routed[1] = edge.getAttributes().createPoint(to.getX(), y2);
                        }
                    } else {
                        routed[0] = edge.getAttributes().createPoint(from.getX(), y2);
                        routed[1] = edge.getAttributes().createPoint(to.getX(), y2);

                        if (targetBounds.contains(routed[0]) || (sourceBounds.contains(routed[0]))
                                || targetBounds.contains(routed[1]) || (sourceBounds.contains(routed[1]))) {
                            routed[0] = edge.getAttributes().createPoint(x2, from.getY());
                            routed[1] = edge.getAttributes().createPoint(x2, to.getY());
                        }
                    }

                    // Set/Add Points
                    for (int i = 0; i < routed.length; i++) {
                        if (!targetBounds.contains(routed[i]) && (!sourceBounds.contains(routed[i]))) {
                            newPoints.add(routed[i]);
                        }
                    }
                }

                // Add target point
                if (target != null) {
                    newPoints.add(target);
                } else {
                    newPoints.add(to);
                }

                return newPoints;
            }

            return null;
        }
    }


    //
    // Default Routing
    //

    /**
     * Class description
     *
     *
     * @version        1.0, 09/09/13
     * @author         Huaxi (Yulin) ZHANG
     */
    public static class LoopRouting implements Edge.Routing {
        public List route(GraphLayoutCache cache, EdgeView edge) {
            if (edge.isLoop()) {
                return routeLoop(cache, edge);
            }

            return routeEdge(cache, edge);
        }

        protected List routeLoop(GraphLayoutCache cache, EdgeView edge) {
            List newPoints = new ArrayList();

            newPoints.add(edge.getSource());

            CellView sourceParent = (edge.getSource() != null)
                                    ? edge.getSource().getParentView()
                                    : edge.getSourceParentView();

            if (sourceParent != null) {
                Point2D     from       = AbstractCellView.getCenterPoint(sourceParent);
                Rectangle2D rect       = sourceParent.getBounds();
                double      width      = rect.getWidth();
                double      height2    = rect.getHeight() / 2;
                double      loopWidth  = Math.min(20, Math.max(10, width / 8));
                double      loopHeight = Math.min(30, Math.max(12, Math.max(loopWidth + 4, height2 / 2)));

                newPoints.add(edge.getAttributes().createPoint(from.getX() - loopWidth,
                        from.getY() - height2 - loopHeight * 1.2));
                newPoints.add(edge.getAttributes().createPoint(from.getX(), from.getY() - height2 - 1.5 * loopHeight));
                newPoints.add(edge.getAttributes().createPoint(from.getX() + loopWidth,
                        from.getY() - height2 - loopHeight * 1.2));
                newPoints.add(edge.getTarget());

                return newPoints;
            }

            return null;
        }

        protected List routeEdge(GraphLayoutCache cache, EdgeView edge) {
            return null;
        }

        public int getPreferredLineStyle(EdgeView edge) {
            if (edge.isLoop()) {
                return getLoopStyle();
            }

            return getEdgeStyle();
        }

        protected int getLoopStyle() {
            return GraphConstants.STYLE_BEZIER;
        }

        protected int getEdgeStyle() {
            return NO_PREFERENCE;
        }

		/* (non-Javadoc)
		 * @see org.jgraph.graph.Edge.Routing#route(org.jgraph.graph.EdgeView)
		 */
		public List route(EdgeView arg0) {
			// TODO Auto-generated method stub
			return null;
		}
    }
}
